{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import csv\n",
    "import math\n",
    "import random\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.metrics.pairwise import cosine_similarity\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "c = 33\n",
    "alpha = 0.00001\n",
    "#获取评分矩阵，并且求得余弦相似度矩阵\n",
    "trainMatrix = pd.read_csv(\"./data/drug_rat18.csv\")#评分矩阵\n",
    "cosSims = cosine_similarity(trainMatrix.T)#余弦相似度矩阵\n",
    "cosSims = torch.from_numpy(cosSims)#将余弦相似度矩阵转为tensor\n",
    "diag = torch.diag(cosSims)             #对角线元素置为0\n",
    "cosSims_diag = torch.diag_embed(diag)  #对角线元素置为0\n",
    "cosSims = cosSims - cosSims_diag       #对角线元素置为0\n",
    "trainMatrix = torch.from_numpy(trainMatrix.values)#评分矩阵转为tensor\n",
    "sims = torch.tensor(np.zeros([len(cosSims),len(cosSims)],float))#最终的相似矩阵，待填充"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def getNiAndH(item):#获取Ni和H\n",
    "    Csim = torch.topk(cosSims[item],c)#获取物品最相关的c个物品的相似度以及索引      \n",
    "    Ni = torch.cat([trainMatrix[:,i] for i in Csim[1]],axis = -1).reshape(c,len(trainMatrix))#获取Ni\n",
    "    H = torch.from_numpy(np.random.rand(c)).reshape(1,c)#随机生成0，1之间的值\n",
    "    return Ni,H,Csim[1]#Csim[1]为最相似物品的索引值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def RMSE(n,Ni,H):\n",
    "    rmse = 0\n",
    "    newMatrix = trainMatrix[:,n].reshape(1,len(trainMatrix)) - torch.mm(H,Ni)\n",
    "    #print(newMatrix)\n",
    "    for value in newMatrix[0]:\n",
    "        rmse += value ** 2\n",
    "    rmse = rmse / len(newMatrix[0])\n",
    "    #print(len(newMatrix[0]))\n",
    "    return float(rmse**0.5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def getLoss(Ni,H,R):\n",
    "    Hloss = []\n",
    "    NH = torch.mm(H,Ni)\n",
    "    return torch.mm((NH - R.reshape(1,len(trainMatrix))),Ni.T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(trainMatrix):\n",
    "    rmse = 0\n",
    "    time_start = time.time()\n",
    "    for item in range(len(trainMatrix[0])):\n",
    "        Ni,H,index = getNiAndH(item)\n",
    "        H = H.double()\n",
    "        Ni = Ni.double()\n",
    "        for i in range(1000):\n",
    "            loss = getLoss(Ni,H,trainMatrix[:,item])\n",
    "            H -= alpha * loss\n",
    "            for j in range(len(H[0])):\n",
    "                if H[0][j] < 0:\n",
    "                    H[0][j] = 0\n",
    "        for i in range(len(H[0])):\n",
    "            sims[item][index[i]] = H[0][i]\n",
    "        rmse += RMSE(item,Ni,H)\n",
    "        time_end = time.time()\n",
    "        if (item + 1) % 100 == 0 or item == len(trainMatrix[0]) - 1:\n",
    "            print(\"进度：\" + str(round((item+1) / len(cosSims) * 100,2)) + \"%   \" + str(item + 1) + \"   平均损失为:\" + str(round(rmse / (item + 1),4)) + \"   耗时\" + str(round(time_end - time_start,2)) + \"s\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def dataNormal(sims):\n",
    "    dataMax = sims.max()\n",
    "    sims = sims / dataMax\n",
    "    return sims"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "进度：10.06%   100   平均损失为:0.4367   耗时63.87s\n",
      "进度：20.12%   200   平均损失为:0.4278   耗时126.61s\n",
      "进度：30.18%   300   平均损失为:0.4251   耗时188.71s\n",
      "进度：40.24%   400   平均损失为:0.4352   耗时250.03s\n",
      "进度：50.3%   500   平均损失为:0.4309   耗时311.64s\n",
      "进度：60.36%   600   平均损失为:0.4334   耗时373.61s\n",
      "进度：70.42%   700   平均损失为:0.433   耗时435.33s\n",
      "进度：80.48%   800   平均损失为:0.4369   耗时496.73s\n",
      "进度：90.54%   900   平均损失为:0.4358   耗时559.1s\n",
      "进度：100.0%   994   平均损失为:0.4345   耗时616.73s\n"
     ]
    }
   ],
   "source": [
    "train(trainMatrix)\n",
    "sims = dataNormal(sims)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "def toFile(sims):\n",
    "    sims = np.array(sims)\n",
    "    sims = pd.DataFrame(sims)\n",
    "    sims.to_csv(\"./result/drug_18sims_c=\"+str(c)+\"迭代1000\"+\".csv\",index = None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "toFile(sims)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
